import json
import os
import random
import time
import traceback

from langchain_core.language_models import BaseChatModel
from selenium import webdriver
from selenium.common import StaleElementReferenceException
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement

from prompt import prompt_template, json_result, roles

# 普通延迟，单位秒
NORMAL_DELAY=os.getenv("NORMAL_DELAY", 0.5)
# 滑动验证延迟，单位秒
SLIDE_DELAY=os.getenv("SLIDE_DELAY", 0.001)


def run(llm: BaseChatModel):
    options = webdriver.EdgeOptions()
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option("useAutomationExtension", False)
    # 去掉window.navigator.webdriver的特性
    options.add_argument("disable-blink-features=AutomationControlled")
    driver = webdriver.Edge(options=options)
    try:
        with open('./stealth.min.js', mode='r') as f:
            js = f.read()

        # 关键代码
        driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": js
        })

        questionnaire_url = os.environ["QUESTIONNAIRE_URL"]
        driver.get(questionnaire_url)

        # driver.get('https://bot.sannysoft.com/')
        # print(driver.title)

        # 标题
        htitle = driver.find_element(by=By.ID, value="htitle")
        title = htitle.text
        print(f"调查报告标题:{htitle.text}")
        # 调查问题
        fieldset1 = driver.find_element(by=By.ID, value="fieldset1")
        fields = fieldset1.find_elements(by=By.XPATH, value="./div")
        print(f"问题数量:{len(fields)}")
        questions = []
        for question in fields:
            text = question.text.split("*")[-1]
            questions.append(text)
        messages = prompt_template.invoke(
            {"role": random.choice(roles), "title": title, "questions": '\n'.join(questions),
             "json_result": json_result})
        fields = [el for el in fields if filter_divs_with_id_format(el)]
        print(f"模型请求:{messages.to_messages()}")
        response = llm.invoke(messages)
        print(f"模型响应:{response}")
        content = json.loads(response.content)
        result = content["result"]
        for i in range(len(fields)):
            print(f"{questions[i]}")
            res = result[i]['content']
            s_type = result[i]['type']
            print(f"答案:{res}\n")
            field = fields[i]
            if 'SELECT' == s_type:
                select(field, res, driver)
            else:
                answer(field, res)
        ctlNext = driver.find_element(by=By.ID, value="ctlNext")
        driver.execute_script("arguments[0].click();", ctlNext)
        time.sleep(NORMAL_DELAY)
        if is_element_present(By.ID, "layui-layer1", driver):  # 是否有智能检测
            auto_check(driver)  # 智能检测
        print(f"填写是否成功{is_element_present(By.ID, "divdsc", driver)}")
    except Exception as e:
        traceback.print_exception(type(e), e, e.__traceback__)
    driver.quit()


def filter_divs_with_id_format(element: WebElement):
    return element.get_attribute("id") is not None and element.get_attribute("id").startswith(
        "div") and element.get_attribute("id")[3:].isdigit()


def auto_check(driver):
    check_modal = driver.find_element(by=By.ID, value="layui-layer1")
    confirm = check_modal.find_element(by=By.XPATH, value=".//a[contains(text(), '确认')]")
    driver.execute_script("arguments[0].click();", confirm)
    time.sleep(NORMAL_DELAY)
    auto_check_bnt = driver.find_element(by=By.ID, value="SM_TXT_1")
    driver.execute_script("arguments[0].click();", auto_check_bnt)
    while is_element_visible_or_exists(auto_check_bnt) and '验证成功' not in auto_check_bnt.text:
        time.sleep(NORMAL_DELAY)
        if is_element_present(By.ID, "nc_1_n1z", driver):  # 检测滑块
            drag_with_curve(driver)


def is_element_visible_or_exists(element):
    try:
        return element.is_displayed()
    except StaleElementReferenceException:
        return False


def drag_with_curve(driver):
    c = driver.find_element(by=By.ID, value="nc_1__scale_text")
    element = driver.find_element(by=By.ID, value="nc_1_n1z")
    total_offset = c.size["width"] - element.size["width"]
    action = ActionChains(driver)
    action.click_and_hold(element).perform()
    steps = 5  # 分10步完成滑动
    l = total_offset / steps
    for i in range(1, steps + 1):
        action.move_by_offset(l, 0).perform()
        time.sleep(SLIDE_DELAY * i)  # 逐步增加延迟
    action.release().perform()


def is_element_present(by, value, driver):
    return len(driver.find_elements(by, value)) > 0


def is_subjective(res: str):
    return ('A' in res or 'B' in res or 'C' in res or 'D' in res) and len(res) <= 4


def select(field: WebElement, res: str, driver: webdriver.Edge):
    for s in res:
        l = field.find_element(By.XPATH, f'.//*[contains(text(), "{s} ")]')
        driver.execute_script("arguments[0].scrollIntoView();", l)
        driver.execute_script("arguments[0].click();", l)
        print(f"选项 {s} 已选中")
        time.sleep(NORMAL_DELAY)


def answer(field: WebElement, res: str):
    input = field.find_element(by=By.TAG_NAME, value="input")
    input.send_keys(res)
